home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Periodicals / develop / develop 4 code / A⁄ROSE / TaskSampleƒ / TaskSample.c next >
Encoding:
C/C++ Source or Header  |  1990-08-28  |  15.9 KB  |  695 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    TaskSample.c
  4. #
  5. #    derived from MPW 3.1's "Sample", a MultiFinder-Aware Simple Sample Application
  6. #
  7. #   Demostrates how to port A/ROSE tasks to the MacOS under MultiFinder
  8. #
  9. #   Refer to the original source code in the MPW CExamples folder for more comments
  10. #
  11. #    Components:
  12. #                TaskSample.c
  13. #                TaskSample.r
  14. #                TaskSample.h
  15. #                TaskSample.make
  16. #
  17. #  MPW build commands:
  18. #
  19.         C TaskSample.c
  20.         Link TaskSample.c.o ∂
  21.             "{CLibraries}"CRuntime.o ∂
  22.             "{CLibraries}"CInterface.o ∂
  23.             "{Libraries}"Interface.o ∂
  24.             "{AROSEPrep}"IPCGlue.o ∂
  25.             -o TaskSample 
  26.         Rez -rd -o TaskSample TaskSample.r -append
  27. #
  28. #
  29. ------------------------------------------------------------------------------*/
  30.  
  31.  
  32.  
  33. #include <Types.h>
  34. #include <Resources.h>
  35. #include <QuickDraw.h>
  36. #include <Fonts.h>
  37. #include <Events.h>
  38. #include <Windows.h>
  39. #include <Menus.h>
  40. #include <TextEdit.h>
  41. #include <Dialogs.h>
  42. #include <Desk.h>
  43. #include <ToolUtils.h>
  44. #include <Memory.h>
  45. #include <SegLoad.h>
  46. #include <Files.h>
  47. #include <OSUtils.h>
  48. #include <OSEvents.h>
  49. #include <DiskInit.h>
  50. #include <Packages.h>
  51. #include <Traps.h>
  52. #include <Strings.h>
  53. #include "TaskSample.h"        // bring in all the #defines for TaskSample 
  54.  
  55.  
  56. //---------------------------------------------------------------------------
  57. // A/ROSE declarations (from "os.h"):
  58.  
  59. typedef long    tid_type;
  60.  
  61. struct mMessage
  62. {
  63.     struct    mMessage    *mNext;
  64.     long                mId;
  65.     short                mCode;
  66.     short                mStatus;
  67.     unsigned    short    mPriority;
  68.     tid_type            mFrom;
  69.     tid_type            mTo;
  70.     unsigned    long    mSData [3];
  71.     unsigned    long    mOData [3];
  72.     long                mDataSize;        // Size of data buffer in bytes. set to negative 
  73.                                         // size of buffer if buffer is shared
  74.                                         // between tasks. eg. Buffer cannot be copied 
  75.     char                *mDataPtr;
  76. };
  77.  
  78. typedef struct mMessage mMessage;
  79.  
  80. // and #define's from "managers.h":
  81.  
  82. #define    OS_UNKNOWN_MESSAGE        (1<<8)    // unknown message                    
  83. #define    Machine_Visible            0        // Register_task Machine visible flag    
  84. //---------------------------------------------------------------------------
  85.  
  86. // global variables
  87.  
  88. SysEnvRec    gMac;                // set up by Initialize 
  89. Boolean        gHasWaitNextEvent;    // set up by Initialize 
  90. Boolean        gInBackground;        // maintained by Initialize and DoEvent 
  91. long        gSleepVal;            // MultiFinder sleep value
  92.                                 // for WaitNextEvent
  93.  
  94. short        gReceived;          // number of received/sent messages 
  95. short        gSent;
  96. long        gTID;                // my task identifier returned by OpenQueue() 
  97. WindowPtr    gMyWindow;            // only one window 
  98.  
  99. // Prototypes for parameter type checking. 
  100.  
  101. void EventLoop( void );
  102. void DoEvent( EventRecord *event );
  103. void DoUpdate( WindowPtr window );
  104. void DoActivate( WindowPtr window, Boolean becomingActive );
  105. void DoContentClick( WindowPtr window );
  106. void DrawWindow( WindowPtr window );
  107. void AdjustMenus( void );
  108. void DoMenuCommand( long menuResult );
  109. Boolean DoCloseWindow( WindowPtr window );
  110. void Terminate( void );
  111. void Initialize( void );
  112. void ForceEnvirons( void );
  113. Boolean IsAppWindow( WindowPtr window );
  114. Boolean IsDAWindow( WindowPtr window );
  115. Boolean TrapAvailable( short tNumber, TrapType tType );
  116. void AlertUser( short error );
  117. void BigBadError( short error );
  118. void NumToHex( long n, short d, char *s);
  119. short AROSEPrep();
  120. void TaskProcessing();
  121.  
  122. // A/ROSE prototypes (from "os.h"):
  123.  
  124. extern    tid_type    OpenQueue(void (*)());
  125. extern    void        CloseQueue();
  126. extern    char        Register_Task(char *, char *, short);
  127. extern    void        Send(mMessage *);
  128. extern    mMessage     *Receive(unsigned long, tid_type, unsigned short, long, ...);
  129. extern  void        FreeMsg();
  130. extern    void        SwapTID(mMessage *);
  131.  
  132. // Define HiWrd and LoWrd macros for efficiency. 
  133. #define HiWrd(aLong)    (((aLong) >> 16) & 0xFFFF)
  134. #define LoWrd(aLong)    ((aLong) & 0xFFFF)
  135.  
  136.  
  137. extern void _DataInit();
  138.  
  139.  
  140. #pragma segment Main
  141. main()
  142. {
  143.     UnloadSeg((Ptr) _DataInit);        // note that _DataInit must not be in Main! 
  144.     
  145.     MaxApplZone();                    // expand the heap so code segments load at the top 
  146.  
  147.     Initialize();                    // initialize the program 
  148.     UnloadSeg((Ptr) Initialize);    // note that Initialize must not be in Main! 
  149.  
  150.     EventLoop();                    // call the main event loop 
  151. }
  152.  
  153.  
  154. #pragma segment Main
  155. void EventLoop()
  156. {
  157.     Boolean        gotEvent;
  158.     EventRecord    event;
  159.  
  160.     do {
  161.             gotEvent = WaitNextEvent(everyEvent, &event, gSleepVal, nil);
  162.         if ( gotEvent )
  163.             DoEvent(&event);
  164.  
  165. //=========================            
  166.         TaskProcessing();  // <<<<<<<<<< this is the A/ROSE task !
  167. //=========================            
  168.         
  169.     } while ( true );    // loop forever; we quit via Terminate/ExitToShell 
  170. } //EventLoop
  171.  
  172.  
  173. #pragma segment Main
  174. void DoEvent(event)
  175.     EventRecord    *event;
  176. {
  177.     short        part, err;
  178.     WindowPtr    window;
  179.     Boolean        hit;
  180.     char        key;
  181.     Point        aPoint;
  182.  
  183.     switch ( event->what ) {
  184.         case mouseDown:
  185.             part = FindWindow(event->where, &window);
  186.             switch ( part ) {
  187.                 case inMenuBar:                // process a mouse menu command (if any) 
  188.                     AdjustMenus();
  189.                     DoMenuCommand(MenuSelect(event->where));
  190.                     break;
  191.                 case inSysWindow:            // let the system handle the mouseDown 
  192.                     SystemClick(event, window);
  193.                     break;
  194.                 case inContent:
  195.                     if ( window != FrontWindow() ) {
  196.                         SelectWindow(window);
  197.                     } else
  198.                         DoContentClick(window);
  199.                     break;
  200.                 case inDrag:                // pass screenBits.bounds to get all gDevices 
  201.                     DragWindow(window, event->where, &qd.screenBits.bounds);
  202.                     break;
  203.                 case inGrow:
  204.                     break;
  205.                 case inZoomIn:
  206.                 case inZoomOut:
  207.                     hit = TrackBox(window, event->where, part);
  208.                     if ( hit ) {
  209.                         SetPort(window);                // the window must be the current port... 
  210.                         EraseRect(&window->portRect);    // because of a bug in ZoomWindow 
  211.                         ZoomWindow(window, part, true);    // note that we invalidate and erase... 
  212.                         InvalRect(&window->portRect);    // to make things look better on-screen 
  213.                     }
  214.                     break;
  215.             }
  216.             break;
  217.         case keyDown:
  218.         case autoKey:                        // check for menukey equivalents 
  219.             key = event->message & charCodeMask;
  220.             if ( event->modifiers & cmdKey )            // Command key down 
  221.                 if ( event->what == keyDown ) {
  222.                     AdjustMenus();                        // enable/disable/check menu items properly 
  223.                     DoMenuCommand(MenuKey(key));
  224.                 }
  225.             break;
  226.         case activateEvt:
  227.             DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
  228.             break;
  229.         case updateEvt:
  230.             DoUpdate((WindowPtr) event->message);
  231.             break;
  232.         case diskEvt:
  233.             if ( HiWord(event->message) != noErr ) {
  234.                 SetPt(&aPoint, kDILeft, kDITop);
  235.                 err = DIBadMount(aPoint, event->message);
  236.             }
  237.             break;
  238.         case kOSEvent:
  239.             switch ((event->message >> 24) & 0x0FF) {        // high byte of message 
  240.                 case kSuspendResumeMessage:        // suspend/resume is also an activate/deactivate 
  241.                     gInBackground = (event->message & kResumeMask) == 0;
  242.                     DoActivate(FrontWindow(), !gInBackground);
  243.                     break;
  244.             }
  245.             break;
  246.     }
  247. } //DoEvent
  248.  
  249.  
  250. #pragma segment Main
  251. void DoUpdate(window)
  252.     WindowPtr    window;
  253. {
  254.     if ( IsAppWindow(window) ) {
  255.         BeginUpdate(window);                // this sets up the visRgn 
  256.         if ( ! EmptyRgn(window->visRgn) )    // draw if updating needs to be done 
  257.             DrawWindow(window);
  258.         EndUpdate(window);
  259.     }
  260. } //DoUpdate
  261.  
  262.  
  263. #pragma segment Main
  264. void DoActivate(window, becomingActive)
  265.     WindowPtr    window;
  266.     Boolean        becomingActive;
  267. {
  268.     if ( IsAppWindow(window) ) {
  269.         if ( becomingActive )
  270.             ; // do whatever you need to at activation 
  271.         else
  272.             ; // do whatever you need to at deactivation 
  273.     }
  274. } //DoActivate
  275.  
  276. #pragma segment Main
  277. void DoContentClick(window)
  278.     WindowPtr    window;
  279. {
  280. #pragma unused (window)
  281.   // do you want to do something here ? 
  282. } //DoContentClick
  283.  
  284.  
  285. #pragma segment Main
  286. void DrawWindow(window)
  287.     WindowPtr    window;
  288. {
  289.     Str255     s;
  290.     Rect    countRect;
  291.     GrafPtr    savePort;
  292.     
  293.     GetPort(&savePort);
  294.     
  295.     SetPort(window);
  296.     TextFont(monaco);
  297.     TextSize(12);
  298.  
  299.     SetRect(&countRect,150,80,200,125);
  300.     EraseRect(&countRect);    // reduce the flicker ... 
  301.  
  302.     GetIndString(s, rWndStrings, 1);
  303.     MoveTo(20,30);
  304.     DrawString(s);
  305.     NumToHex(gTID, 8, s);
  306.     DrawString(s);
  307.  
  308.     GetIndString(s, rWndStrings, 2);
  309.     MoveTo(20,50);
  310.     DrawString(s);
  311.  
  312.     GetIndString(s, rWndStrings, 3);
  313.     MoveTo(20,100);
  314.     DrawString(s);
  315.     NumToString(gReceived, s);
  316.     DrawString(s);
  317.  
  318.     GetIndString(s, rWndStrings, 4);
  319.     MoveTo(20,120);
  320.     DrawString(s);
  321.     NumToString(gSent, s);
  322.     DrawString(s);
  323.     
  324.     SetPort(savePort);
  325.  
  326. } //DrawWindow
  327.  
  328.  
  329. #pragma segment Main
  330. void AdjustMenus()
  331. {
  332.     WindowPtr    window;
  333.     MenuHandle    menu;
  334.  
  335.     window = FrontWindow();
  336.  
  337.     menu = GetMHandle(mFile);
  338.     if ( IsDAWindow(window) )        // we can allow desk accessories to be closed from the menu 
  339.         EnableItem(menu, iClose);
  340.     else
  341.         DisableItem(menu, iClose);    // but not our only window 
  342.  
  343.     menu = GetMHandle(mEdit);
  344.     if ( IsDAWindow(window) ) {        // a desk accessory might need the edit menu… 
  345.         EnableItem(menu, iUndo);
  346.         EnableItem(menu, iCut);
  347.         EnableItem(menu, iCopy);
  348.         EnableItem(menu, iClear);
  349.         EnableItem(menu, iPaste);
  350.     } else {                        // …but we don’t use it 
  351.         DisableItem(menu, iUndo);
  352.         DisableItem(menu, iCut);
  353.         DisableItem(menu, iCopy);
  354.         DisableItem(menu, iClear);
  355.         DisableItem(menu, iPaste);
  356.     }
  357.     menu = GetMHandle(mSleep);
  358.     if (gSleepVal == 0) {
  359.         CheckItem(menu,iNoSleep,true);
  360.         CheckItem(menu,i60Ticks,false);
  361.     }
  362.     else  {
  363.         CheckItem(menu,iNoSleep,false);
  364.         CheckItem(menu,i60Ticks,true);
  365.     }
  366. } //AdjustMenus
  367.  
  368.  
  369. #pragma segment Main
  370. void DoMenuCommand(menuResult)
  371.     long        menuResult;
  372. {
  373.     short        menuID;                // the resource ID of the selected menu 
  374.     short        menuItem;            // the item number of the selected menu 
  375.     short        itemHit;
  376.     Str255        daName;
  377.     short        daRefNum;
  378.     Boolean        handledByDA;
  379.  
  380.     menuID = HiWord(menuResult);    // use macros for efficiency to... 
  381.     menuItem = LoWord(menuResult);    // get menu item number and menu number 
  382.     switch ( menuID ) {
  383.         case mApple:
  384.             switch ( menuItem ) {
  385.                 case iAbout:        // bring up alert for About 
  386.                     itemHit = Alert(rAboutAlert, nil);
  387.                     break;
  388.                 default:            // all non-About items in this menu are DAs 
  389.                     // type Str255 is an array in MPW 3 
  390.                     GetItem(GetMHandle(mApple), menuItem, daName);
  391.                     daRefNum = OpenDeskAcc(daName);
  392.                     break;
  393.             }
  394.             break;
  395.         case mFile:
  396.             switch ( menuItem ) {
  397.                 case iClose:
  398.                     DoCloseWindow(FrontWindow());
  399.                     break;
  400.                 case iQuit:
  401.                     Terminate();
  402.                     break;
  403.             }
  404.             break;
  405.         case mEdit:                    // call SystemEdit for DA editing & MultiFinder 
  406.             handledByDA = SystemEdit(menuItem-1);    // since we don’t do any Editing 
  407.             break;
  408.         case mSleep:
  409.             switch ( menuItem ) {
  410.                 case iNoSleep:
  411.                     gSleepVal = 0;
  412.                     break;
  413.                 case i60Ticks:
  414.                     gSleepVal = 60;
  415.                     break;
  416.             }
  417.             break;
  418.     }
  419.     HiliteMenu(0);                    // unhighlight what MenuSelect (or MenuKey) hilited 
  420. } //DoMenuCommand
  421.  
  422.  
  423.  
  424. #pragma segment Main
  425. Boolean DoCloseWindow(window)
  426.     WindowPtr    window;
  427. {
  428.     if ( IsDAWindow(window) )
  429.         CloseDeskAcc(((WindowPeek) window)->windowKind);
  430.     else if ( IsAppWindow(window) )
  431.         CloseWindow(window);
  432.     return true;
  433. } //DoCloseWindow
  434.  
  435.  
  436.  
  437. #pragma segment Main
  438. void Terminate()
  439. {
  440.     WindowPtr    aWindow;
  441.     Boolean        closed;
  442.     
  443.     CloseQueue();        // Clean up interaction with A/ROSE Prep 
  444.     closed = true;
  445.     do {
  446.         aWindow = FrontWindow();                // get the current front window 
  447.         if (aWindow != nil)
  448.             closed = DoCloseWindow(aWindow);    // close this window     
  449.     }
  450.     while (closed && (aWindow != nil));
  451.     if (closed)
  452.         ExitToShell();                            // exit if no cancellation 
  453. } //Terminate
  454.  
  455.  
  456.  
  457. #pragma segment Initialize
  458. void Initialize()
  459. {
  460.     Handle        menuBar;
  461.     Ptr            windowP;
  462.     long        total, contig;
  463.     EventRecord event;
  464.     short        count, result;
  465.  
  466.     gInBackground = false;
  467.     gSleepVal = 60;
  468.     
  469.     InitGraf((Ptr) &qd.thePort);
  470.     InitFonts();
  471.     InitWindows();
  472.     InitMenus();
  473.     TEInit();
  474.     InitDialogs(nil);
  475.     InitCursor();
  476.     
  477.     //    This next bit of code is necessary to allow the default button of our
  478.     //    alert be outlined. 
  479.      
  480.     for (count = 1; count <= 3; count++)
  481.         EventAvail(everyEvent, &event);
  482.     
  483.     SysEnvirons(kSysEnvironsVersion, &gMac);
  484.     
  485.     if (gMac.machineType < 0)
  486.         BigBadError(eWrongMachine);
  487.     
  488.     if (! TrapAvailable(_WaitNextEvent, ToolTrap))
  489.         BigBadError(eWrongMachine);
  490.  
  491.     if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap)
  492.         BigBadError(eSmallSize);
  493.     
  494.     // ZeroScrap(); 
  495.  
  496.     PurgeSpace(&total, &contig);
  497.     if (total < kMinSpace)
  498.         BigBadError(eNoMemory);
  499.  
  500.     result = AROSEPrep();
  501.     if (result != noErr)
  502.         BigBadError(result);
  503.  
  504.     windowP = NewPtr(sizeof(WindowRecord));
  505.     if ( windowP == nil )
  506.         BigBadError(eNoWindow);
  507.  
  508.     gMyWindow = GetNewWindow(rWindow, windowP, (WindowPtr) -1);
  509.     if ( gMyWindow == nil )
  510.         BigBadError(eNoWindow);
  511.  
  512.     menuBar = GetNewMBar(rMenuBar);            // read menus into menu bar 
  513.     if ( menuBar == nil )
  514.         BigBadError(eNoMemory);
  515.     SetMenuBar(menuBar);                    // install menus 
  516.     DisposHandle(menuBar);
  517.     AddResMenu(GetMHandle(mApple), 'DRVR');    // add DA names to Apple menu 
  518.     DrawMenuBar();
  519.     
  520. } //Initialize
  521.  
  522.  
  523. #pragma segment Main
  524. Boolean IsAppWindow(window)
  525.     WindowPtr    window;
  526. {
  527.     short        windowKind;
  528.  
  529.     if ( window == nil )
  530.         return false;
  531.     else {    // application windows have windowKinds = userKind (8) 
  532.         windowKind = ((WindowPeek) window)->windowKind;
  533.         return (windowKind = userKind);
  534.     }
  535. } //IsAppWindow
  536.  
  537.  
  538. #pragma segment Main
  539. Boolean IsDAWindow(window)
  540.     WindowPtr    window;
  541. {
  542.     if ( window == nil )
  543.         return false;
  544.     else    // DA windows have negative windowKinds 
  545.         return ((WindowPeek) window)->windowKind < 0;
  546. } //IsDAWindow
  547.  
  548.  
  549. #pragma segment Initialize
  550. Boolean TrapAvailable(tNumber,tType)
  551.     short        tNumber;
  552.     TrapType    tType;
  553. {
  554.     if ( ( tType == ToolTrap ) &&
  555.         ( gMac.machineType > envMachUnknown ) &&
  556.         ( gMac.machineType < envMacII ) ) {        // it's a 512KE, Plus, or SE 
  557.         tNumber = tNumber & 0x03FF;
  558.         if ( tNumber > 0x01FF )                    // which means the tool traps 
  559.             tNumber = _Unimplemented;            // only go to 0x01FF 
  560.     }
  561.     return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
  562. } //TrapAvailable
  563.  
  564.  
  565. #pragma segment Main
  566. void AlertUser( short error)
  567. {
  568.     short        itemHit;
  569.     Str255        errMsg;
  570.  
  571.     SetCursor(&qd.arrow);
  572.     if ((error>0)&&(error<=kLastErrStr)) {
  573.         GetIndString(errMsg, rErrStrings, error);
  574.         ParamText(errMsg, "", "", "");
  575.     }
  576.     else {
  577.         NumToString(error, &errMsg);
  578.         ParamText("\pUnknown error", errMsg, "", "");
  579.     }
  580.         
  581.     itemHit = Alert(rUserAlert, nil);
  582. } // AlertUser 
  583.  
  584. #pragma segment Main
  585. void BigBadError( short error)
  586. {
  587.     AlertUser(error);
  588.     if (gTID>0)
  589.         CloseQueue();
  590.     ExitToShell();
  591. }
  592.  
  593.  
  594. #pragma segment Main
  595. void NumToHex( long n, short d, char *s)   // I hate this @#%@ !
  596.     // s must point to at least d+1 free bytes - returns Pascal String of length d
  597.     // requires d > 0
  598. {
  599.     short c;
  600.     
  601.     s[0] = d;
  602.     do {
  603.         c =  (n & 0x0F) + 48;    // not so Script Manager compatible
  604.                                 // (as usual at this programming level...)
  605.         if (c>57)
  606.             c = c+7;
  607.         s[d--] = (char) c;
  608.         n = n>>4;
  609.     } while (d > 0);
  610. }
  611.  
  612. #pragma segment Main
  613. void StringCopy(char *s, char *t)
  614. {
  615.     short n= (*t++ = *s++);
  616.     while (n-- >0)
  617.         *t++ = *s++;
  618. }
  619.  
  620. #pragma segment Initialize
  621. short AROSEPrep()
  622. {
  623.     StringHandle sh;
  624.     Str255 taskName = "?", taskType = "?";
  625.  
  626.     sh = GetString(rTaskName);
  627.     if (sh != nil)
  628.         StringCopy(*sh,taskName);
  629.     sh = GetString(rTaskType);
  630.     if (sh != nil)
  631.         StringCopy(*sh,taskType);
  632.     
  633.     gTID = OpenQueue(0);
  634.     if (gTID == 0) {
  635.         return(eNoAROSE);
  636.     }
  637.     if (!Register_Task (p2cstr(&taskName), p2cstr(&taskType), Machine_Visible)) {
  638.         CloseQueue(); // better clean up - we are going to Exit 
  639.         return(eRegister);
  640.     }
  641.     return(noErr);
  642. }
  643.  
  644.  
  645. #pragma segment Main
  646. void TaskProcessing()
  647. {
  648.     mMessage *m;
  649.     GrafPtr savePort;
  650.     
  651.     
  652.     m = Receive(0L, 0L, 0L, -1L, nil);  // -1L: return without waiting (OS_NO_TIMEOUT)
  653.                                         // nil: no completion routine
  654.                                         
  655. // in A/ROSE this would be:    m = Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, OS_NO_TIMEOUT);
  656.     if (m) {
  657.         gReceived++;
  658.  
  659.         if (m->mStatus != 0)     {    // message was considered undeliverable
  660.             AlertUser(eUndeliverable);
  661.             FreeMsg(m);                // of no use any more: give it back to A/ROSE
  662.         }
  663.         else {
  664.             switch (m->mCode)    {
  665.             case DUMMYCODE:
  666.                 break;
  667.             case RESETCODE:
  668.                 gReceived = 0;
  669.                 gSent = -1; // want to have =0 after gSent++ below
  670.                 break;
  671.     
  672.             // handle here all the message codes you specified in your design ! 
  673.             
  674.             default:
  675.                 AlertUser(eUnknownMsg);
  676.                 m->mCode |= 0x8000;    // unrecognized message code;
  677.                 m->mStatus = OS_UNKNOWN_MESSAGE; // defined in "managers.h"
  678.                 break;
  679.             }
  680.             // send message back 
  681.             SwapTID(m);
  682.             m->mCode++;
  683.             Send (m);
  684.             gSent++;
  685.             
  686.             GetPort(&savePort);
  687.             SetPort(gMyWindow);
  688.             
  689.             InvalRect(&(gMyWindow->portBits.bounds)); // update displayInvalRectInvalRect(r);
  690.             
  691.             SetPort(savePort);
  692.         }
  693.     }        
  694. }
  695.